package gr.uop.cst.context.base;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static gr.uop.cst.context.base.ContextValueStatus.*;

/**
 *
 * @author Ioannis Bourlakos &lt;mi49@uop.gr&gt;
 */
final class ContextObjectWorker<T> implements Activatable  {
    // TODO: redefine class to support minimum validity and refresh periods
    // TODO: make sure its worthy having replicate values, instead of implementing this class inside ContextObject.
    // TODO: create a dynamic ContextObjectWorkerContainer

    private final ContextObject<T> contextObject;
    private final ContextProvider<T> contextProvider;
    private final Refresher refresher;
    private final long maxRefreshPeriod;
    private final Validator validator;
    private final long maxValidityPeriod;
    private final ScheduledExecutorService scheduler;
    //private ContextValueEvent<T> eventObject;
    private boolean active;
    

    ContextObjectWorker(ContextObject<T> obj) {
        if (obj == null) {
            throw new IllegalArgumentException("ContextObject cannot be null.");
        }
        this.active = false;
        this.contextObject = obj;
        this.contextProvider = obj.getContextProvider();
        this.refresher = new Refresher();
        this.maxRefreshPeriod = obj.getMaxRefreshPeriod();
        this.validator = new Validator();
        this.maxValidityPeriod = obj.getMaxValidityPeriod();
        // TODO: maybe a custom-tuned threadpool?
        this.scheduler = Executors.newScheduledThreadPool(2);
        //this.eventObject = null;
        //System.err.println("ContextObjectWorker::Max refresh period: " + maxRefreshPeriod);
    }

    public final void activate() {
        this.scheduler.scheduleAtFixedRate(refresher, 0, maxRefreshPeriod, TimeUnit.MILLISECONDS);
        this.scheduler.scheduleAtFixedRate(validator, maxValidityPeriod, maxValidityPeriod, TimeUnit.MILLISECONDS);
        this.active = true;
    }

    public final void deactivate() {
        scheduler.shutdownNow();
        this.active = false;
    }

    public boolean isActive() {
        return active;
    }

    private final class Refresher implements Runnable {

        @Override
        public final void run() {
            // request new value
//            System.err.println("refreshing context value"); 
            T newValue = null;
            ContextSource newSource = null;
            contextObject.fireValueRequested(new ContextValueEvent<T>(contextObject));
            try {
                try {
                    newValue = contextProvider.getCurrentValue();
                    newSource = contextProvider.getCurrentSource();
                } catch (ContextProviderUnavailableException ex) {
                    throw new ContextValueUnavailableException("ContextProvider is unavailable.");
                }
                // new value available - in sync: update the context object;
                // we don't want a Validator thread to interfere while this
                // takes place
                synchronized (contextObject) {
                    contextObject.setCurrentValue(newValue);
                    contextObject.setCurrentSource(newSource);
                    contextObject.setCurrentTimestamp(System.currentTimeMillis());
                    if (!contextObject.isValid()) {
                        //System.err.println("got valid."); //TODO
                        contextObject.setCurrentValueStatus(VALID);
                        contextObject.fireValueValid(new ContextValueEvent<T>(contextObject,newValue));
                    }
                    contextObject.fireValueRefreshed(new ContextValueEvent<T>(contextObject,newValue));
                }
            } catch (ContextValueUnavailableException ex) {
                // new value not available
                if (!contextObject.isUnavailable()) {
//                    System.err.println("got unavailable."); //TODO
                    contextObject.setCurrentValueStatus(UNAVAILABLE);
                    contextObject.fireValueUnavailable(new ContextValueEvent<T>(contextObject));
                }
            }
        }
    }

    private final class Validator implements Runnable {

        @Override
        public final void run() {
            // a validation check is about to take place;
            // we don't want a Refresher thread to interfere (yet)
            synchronized (contextObject) {
                long now = System.currentTimeMillis();
                long lastTimestamp = contextObject.getCurrentTimestamp();
                long timeDelta = now - lastTimestamp;
                boolean isValid = contextObject.isValid();
                boolean timedOut = (timeDelta > maxValidityPeriod);
                if (isValid && timedOut) {
                    contextObject.setCurrentValueStatus(OBSOLETE);
//                    System.err.println("got obsolete."); //TODO
                    contextObject.fireValueObsolete(new ContextValueEvent<T>(contextObject));
                }
            }
        }
    }
}
